package cn.template.config;

import cn.template.common.JwtTokenUtil;
import cn.template.models.common.JwtUser;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.var;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtRequestFilter extends OncePerRequestFilter {
    private static final Logger logger = LogManager.getLogger(JwtRequestFilter.class);

    private final JwtTokenUtil jwtTokenUtil;

    public JwtRequestFilter(JwtTokenUtil jwtTokenUtil) {
        this.jwtTokenUtil = jwtTokenUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        final String authToken = request.getHeader("Authorization");
        final String url = request.getRequestURI();

        JwtUser user = null;
        if (authToken != null) {
            // JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token
            if (authToken.startsWith("Bearer ")) {
                String jwtToken = authToken.substring(7);
                try {
                    user = jwtTokenUtil.getUserFromToken(jwtToken);
                } catch (IllegalArgumentException e) {
                    logger.warn(String.format("Unable to get JWT Token, request: %s", url));
                } catch (ExpiredJwtException e) {
                    logger.warn(String.format("JWT Token has expired, request: %s", url));
                } catch (RuntimeException e) {
                    logger.error(String.format("request: %s, Bad token: %s, message: %s", url, authToken, e.getMessage()));
                }
            } else {
                logger.warn(String.format("JWT Token does not begin with Bearer String, request: %s, token:%s", url, authToken));
            }
        }

        //Once we get the token validate it.
        if (user != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            // do some special validation if it in needs
            // if (jwtTokenUtil.validateToken(jwtToken, user)) {}

            var usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
            usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            // After setting the Authentication in the context, we specify
            // that the current user is authenticated. So it passes the Spring Security Configurations successfully.
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        }

        chain.doFilter(request, response);
    }
}
